// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef V8_X64_REGISTER_X64_H_
#define V8_X64_REGISTER_X64_H_

#include "src/register.h"
#include "src/reglist.h"

namespace v8 {
namespace internal {

#define GENERAL_REGISTERS(V) \
    V(rax)                   \
    V(rcx)                   \
    V(rdx)                   \
    V(rbx)                   \
    V(rsp)                   \
    V(rbp)                   \
    V(rsi)                   \
    V(rdi)                   \
    V(r8)                    \
    V(r9)                    \
    V(r10)                   \
    V(r11)                   \
    V(r12)                   \
    V(r13)                   \
    V(r14)                   \
    V(r15)

#define ALLOCATABLE_GENERAL_REGISTERS(V) \
    V(rax)                               \
    V(rbx)                               \
    V(rdx)                               \
    V(rcx)                               \
    V(rsi)                               \
    V(rdi)                               \
    V(r8)                                \
    V(r9)                                \
    V(r11)                               \
    V(r12)                               \
    V(r14)                               \
    V(r15)

    enum RegisterCode {
#define REGISTER_CODE(R) kRegCode_##R,
        GENERAL_REGISTERS(REGISTER_CODE)
#undef REGISTER_CODE
            kRegAfterLast
    };

    class Register : public RegisterBase<Register, kRegAfterLast> {
    public:
        bool is_byte_register() const { return reg_code_ <= 3; }
        // Return the high bit of the register code as a 0 or 1.  Used often
        // when constructing the REX prefix byte.
        int high_bit() const { return reg_code_ >> 3; }
        // Return the 3 low bits of the register code.  Used when encoding registers
        // in modR/M, SIB, and opcode bytes.
        int low_bits() const { return reg_code_ & 0x7; }

    private:
        friend class RegisterBase<Register, kRegAfterLast>;
        explicit constexpr Register(int code)
            : RegisterBase(code)
        {
        }
    };

    ASSERT_TRIVIALLY_COPYABLE(Register);
    static_assert(sizeof(Register) == sizeof(int),
        "Register can efficiently be passed by value");

#define DECLARE_REGISTER(R) \
    constexpr Register R = Register::from_code<kRegCode_##R>();
    GENERAL_REGISTERS(DECLARE_REGISTER)
#undef DECLARE_REGISTER
    constexpr Register no_reg = Register::no_reg();

    constexpr int kNumRegs = 16;

    constexpr RegList kJSCallerSaved = Register::ListOf<rax, rcx, rdx,
        rbx, // used as a caller-saved register in JavaScript code
        rdi // callee function
        >();

    constexpr int kNumJSCallerSaved = 5;

    // Number of registers for which space is reserved in safepoints.
    constexpr int kNumSafepointRegisters = 16;

#ifdef _WIN64
    // Windows calling convention
    constexpr Register arg_reg_1 = rcx;
    constexpr Register arg_reg_2 = rdx;
    constexpr Register arg_reg_3 = r8;
    constexpr Register arg_reg_4 = r9;
#else
    // AMD64 calling convention
    constexpr Register arg_reg_1 = rdi;
    constexpr Register arg_reg_2 = rsi;
    constexpr Register arg_reg_3 = rdx;
    constexpr Register arg_reg_4 = rcx;
#endif // _WIN64

#define DOUBLE_REGISTERS(V) \
    V(xmm0)                 \
    V(xmm1)                 \
    V(xmm2)                 \
    V(xmm3)                 \
    V(xmm4)                 \
    V(xmm5)                 \
    V(xmm6)                 \
    V(xmm7)                 \
    V(xmm8)                 \
    V(xmm9)                 \
    V(xmm10)                \
    V(xmm11)                \
    V(xmm12)                \
    V(xmm13)                \
    V(xmm14)                \
    V(xmm15)

#define FLOAT_REGISTERS DOUBLE_REGISTERS
#define SIMD128_REGISTERS DOUBLE_REGISTERS

#define ALLOCATABLE_DOUBLE_REGISTERS(V) \
    V(xmm0)                             \
    V(xmm1)                             \
    V(xmm2)                             \
    V(xmm3)                             \
    V(xmm4)                             \
    V(xmm5)                             \
    V(xmm6)                             \
    V(xmm7)                             \
    V(xmm8)                             \
    V(xmm9)                             \
    V(xmm10)                            \
    V(xmm11)                            \
    V(xmm12)                            \
    V(xmm13)                            \
    V(xmm14)

    constexpr bool kPadArguments = false;
    constexpr bool kSimpleFPAliasing = true;
    constexpr bool kSimdMaskRegisters = false;

    enum DoubleRegisterCode {
#define REGISTER_CODE(R) kDoubleCode_##R,
        DOUBLE_REGISTERS(REGISTER_CODE)
#undef REGISTER_CODE
            kDoubleAfterLast
    };

    class XMMRegister : public RegisterBase<XMMRegister, kDoubleAfterLast> {
    public:
        // Return the high bit of the register code as a 0 or 1.  Used often
        // when constructing the REX prefix byte.
        int high_bit() const { return reg_code_ >> 3; }
        // Return the 3 low bits of the register code.  Used when encoding registers
        // in modR/M, SIB, and opcode bytes.
        int low_bits() const { return reg_code_ & 0x7; }

    private:
        friend class RegisterBase<XMMRegister, kDoubleAfterLast>;
        explicit constexpr XMMRegister(int code)
            : RegisterBase(code)
        {
        }
    };

    ASSERT_TRIVIALLY_COPYABLE(XMMRegister);
    static_assert(sizeof(XMMRegister) == sizeof(int),
        "XMMRegister can efficiently be passed by value");

    typedef XMMRegister FloatRegister;

    typedef XMMRegister DoubleRegister;

    typedef XMMRegister Simd128Register;

#define DECLARE_REGISTER(R) \
    constexpr DoubleRegister R = DoubleRegister::from_code<kDoubleCode_##R>();
    DOUBLE_REGISTERS(DECLARE_REGISTER)
#undef DECLARE_REGISTER
    constexpr DoubleRegister no_dreg = DoubleRegister::no_reg();

    // Define {RegisterName} methods for the register types.
    DEFINE_REGISTER_NAMES(Register, GENERAL_REGISTERS)
    DEFINE_REGISTER_NAMES(XMMRegister, DOUBLE_REGISTERS)

    // Give alias names to registers for calling conventions.
    constexpr Register kReturnRegister0 = rax;
    constexpr Register kReturnRegister1 = rdx;
    constexpr Register kReturnRegister2 = r8;
    constexpr Register kJSFunctionRegister = rdi;
    constexpr Register kContextRegister = rsi;
    constexpr Register kAllocateSizeRegister = rdx;
    constexpr Register kSpeculationPoisonRegister = r12;
    constexpr Register kInterpreterAccumulatorRegister = rax;
    constexpr Register kInterpreterBytecodeOffsetRegister = r9;
    constexpr Register kInterpreterBytecodeArrayRegister = r14;
    constexpr Register kInterpreterDispatchTableRegister = r15;

    constexpr Register kJavaScriptCallArgCountRegister = rax;
    constexpr Register kJavaScriptCallCodeStartRegister = rcx;
    constexpr Register kJavaScriptCallTargetRegister = kJSFunctionRegister;
    constexpr Register kJavaScriptCallNewTargetRegister = rdx;
    constexpr Register kJavaScriptCallExtraArg1Register = rbx;

    constexpr Register kRuntimeCallFunctionRegister = rbx;
    constexpr Register kRuntimeCallArgCountRegister = rax;
    constexpr Register kRuntimeCallArgvRegister = r15;
    constexpr Register kWasmInstanceRegister = rsi;

    // Default scratch register used by MacroAssembler (and other code that needs
    // a spare register). The register isn't callee save, and not used by the
    // function calling convention.
    constexpr Register kScratchRegister = r10;
    constexpr XMMRegister kScratchDoubleReg = xmm15;
    constexpr Register kRootRegister = r13; // callee save

    constexpr Register kOffHeapTrampolineRegister = kScratchRegister;

} // namespace internal
} // namespace v8

#endif // V8_X64_REGISTER_X64_H_
